package com.power.sifa.correction.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import com.plf.common.cache.redis.RedisCacheUtil;
import com.power.sifa.correction.dao.*;
import com.power.sifa.correction.model.*;
import com.power.sifa.correction.model.enumeration.Tp_crc_ruleEnum;
import com.power.sifa.correction.model.pojo.bo.CheckRuleRedisBO;
import com.power.sifa.correction.model.pojo.bo.CheckRuleRequestBO;
import com.power.sifa.correction.service.Crc_groupService;
import com.power.sifa.correction.service.Crc_personService;
import com.power.sifa.correction.service.Crw_rule_geopointService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @ClassName RuleRecordCacheService
 * @Author xiongzw
 * @Date 2023/12/15 16:30
 * @Description  规则缓存service
 * @Version: 1.0
 **/

@Component
@Slf4j
public class RuleRecordCacheService {

    /**
     * redis 存储key, 加上人员id
     */
    private static final String REDIS_KEY = "CORRECTPERSON:";

    private static Interner<String> lock = Interners.newWeakInterner();

    @Resource
    private Crc_personService crc_personService;
    @Resource
    private Crc_groupService crc_groupService;
    @Resource
    private Crw_rule_personDao crw_rule_personDao;
    @Resource
    private Crw_rule_groupDao crw_rule_groupDao;
    @Resource
    private Crw_rule_disabledDao crw_rule_disabledDao;
    @Resource
    private Crw_rule_zoneoutDao crw_rule_zoneoutDao;
    @Resource
    private Crw_rule_zoneinDao crw_rule_zoneinDao;
    @Resource
    private Crw_rule_nightDao crw_rule_nightDao;
    @Resource
    private Crw_rule_samecaseDao crw_rule_samecaseDao;
    @Resource
    private Crw_rule_geopointService crw_rule_geopointService;
    @Resource
    private RedisCacheUtil redisCacheUtil;

    /**
     * 进行规则分组
     * @param ruleSet
     */
    private Map<Integer, List<String>> splitRuleGroup(Set<String> ruleSet) {
        Map<Integer, List<String>> ruleGroup = new HashMap<>();
        // 进行规则分类
        ruleSet.stream().forEach(ruleId -> {
            String firstLetter = ruleId.substring(0,1).toUpperCase(Locale.ROOT);
            switch (firstLetter) {
                case "A":
                    List<String> zoneOutRuleIds = CollectionUtil.isNotEmpty(ruleGroup.get(Tp_crc_ruleEnum.ZONE_OUT_RULE.getId())) ?
                            ruleGroup.get(Tp_crc_ruleEnum.ZONE_OUT_RULE.getId()) : new ArrayList<>();
                    zoneOutRuleIds.add(ruleId);
                    ruleGroup.put(Tp_crc_ruleEnum.ZONE_OUT_RULE.getId(), zoneOutRuleIds);
                    break;
                case "B":
                    List<String> zoneInRuleIds = CollectionUtil.isNotEmpty(ruleGroup.get(Tp_crc_ruleEnum.ZONE_IN_RULE.getId())) ?
                            ruleGroup.get(Tp_crc_ruleEnum.ZONE_IN_RULE.getId()) : new ArrayList<>();
                    zoneInRuleIds.add(ruleId);
                    ruleGroup.put(Tp_crc_ruleEnum.ZONE_IN_RULE.getId(), zoneInRuleIds);
                    break;
                case "C":
                    List<String> nightOutRuleIds =  CollectionUtil.isNotEmpty(ruleGroup.get(Tp_crc_ruleEnum.NIGHT_OUT_RULE.getId())) ?
                            ruleGroup.get(Tp_crc_ruleEnum.NIGHT_OUT_RULE.getId()) : new ArrayList<>();
                    nightOutRuleIds.add(ruleId);
                    ruleGroup.put(Tp_crc_ruleEnum.NIGHT_OUT_RULE.getId(), nightOutRuleIds);
                    break;
                  // FEAT: 弃用同案犯规则，暴露避免后续追加
//                case "D":
//                    List<String> sameCaseRuleIds = CollectionUtil.isNotEmpty(ruleGroup.get(Tp_crc_ruleEnum.SAME_CASE_RULE.getId())) ?
//                            ruleGroup.get(Tp_crc_ruleEnum.SAME_CASE_RULE.getId()) : new ArrayList<>();
//                    sameCaseRuleIds.add(ruleId);
//                    ruleGroup.put(Tp_crc_ruleEnum.SAME_CASE_RULE.getId(), sameCaseRuleIds);
//                    break;
            }
        });
        return ruleGroup;
    }

    /**
     * 初始化规则设置
     *
     * @param ruleGroup
     */
    private void initRuleWithGroup(Map<Integer, List<String>> ruleGroup, CheckRuleRequestBO checkRuleRequestBO) {
        if (ObjectUtil.isNotNull(ruleGroup)) {
            for (Integer key : ruleGroup.keySet()) {
                if (Tp_crc_ruleEnum.ZONE_OUT_RULE.getId().equals(key)) {
                    List<Crw_rule_zoneout> zoneOutRules = crw_rule_zoneoutDao.findByRuleIds(ruleGroup.get(key));
                    if (CollectionUtil.isNotEmpty(zoneOutRules)) {
                        // 查询对应规则涉及的区域坐标
                        zoneOutRules.stream().forEach(element -> element.setGeopoint(crw_rule_geopointService.findGeopointsByRuleId(element.getId())));
                    }
                    checkRuleRequestBO.setZoneOutRules(zoneOutRules);
                }
                if (Tp_crc_ruleEnum.ZONE_IN_RULE.getId().equals(key)) {
                    List<Crw_rule_zonein> zoneInRules = crw_rule_zoneinDao.findByRuleIds(ruleGroup.get(key));
                    if (CollectionUtil.isNotEmpty(zoneInRules)) {
                        // 查询对应规则涉及的区域坐标
                        zoneInRules.stream().forEach(element -> element.setGeopoint(crw_rule_geopointService.findGeopointsByRuleId(element.getId())));
                    }
                    checkRuleRequestBO.setZoneInRules(zoneInRules);
                }
                if (Tp_crc_ruleEnum.NIGHT_OUT_RULE.getId().equals(key)) {
                    List<Crw_rule_night> nightOutRules = crw_rule_nightDao.findByRuleIds(ruleGroup.get(key));
                    checkRuleRequestBO.setNightOutRules(nightOutRules);
                }
                // FEAT: 弃用同案犯规则，暴露避免后续追加
//                if (Tp_crc_ruleEnum.SAME_CASE_RULE.getId().equals(key)) {
//                    List<Crw_rule_samecase> samecaseRules = crw_rule_samecaseDao.findByRuleIds(ruleGroup.get(key));
//                    checkRuleRequestBO.setSameCaseRules(samecaseRules);
//                }
            }
        }
    }



    /**
     * 初始化规则设置 用于缓存对象
     * @param ruleGroup
     */
    private void initRuleWithGroupRedis(Map<Integer, List<String>> ruleGroup, CheckRuleRedisBO checkRuleRedis) {
        if (ObjectUtil.isNotNull(ruleGroup)) {
            for (Integer key : ruleGroup.keySet()) {
                if (Tp_crc_ruleEnum.ZONE_OUT_RULE.getId().equals(key)) {
                    List<Crw_rule_zoneout> zoneOutRules = crw_rule_zoneoutDao.findByRuleIds(ruleGroup.get(key));
                    if (CollectionUtil.isNotEmpty(zoneOutRules)) {
                        // 查询对应规则涉及的区域坐标
                        zoneOutRules.stream().forEach(element -> element.setGeopoint(crw_rule_geopointService.findGeopointsByRuleId(element.getId())));
                    }
                    checkRuleRedis.setZoneOutRules(zoneOutRules);
                }
                if (Tp_crc_ruleEnum.ZONE_IN_RULE.getId().equals(key)) {
                    List<Crw_rule_zonein> zoneInRules = crw_rule_zoneinDao.findByRuleIds(ruleGroup.get(key));
                    if (CollectionUtil.isNotEmpty(zoneInRules)) {
                        // 查询对应规则涉及的区域坐标
                        zoneInRules.stream().forEach(element -> element.setGeopoint(crw_rule_geopointService.findGeopointsByRuleId(element.getId())));
                    }
                    checkRuleRedis.setZoneInRules(zoneInRules);
                }
                if (Tp_crc_ruleEnum.NIGHT_OUT_RULE.getId().equals(key)) {
                    List<Crw_rule_night> nightOutRules = crw_rule_nightDao.findByRuleIds(ruleGroup.get(key));
                    checkRuleRedis.setNightOutRules(nightOutRules);
                }
                // FEAT: 弃用同案犯规则，暴露避免后续追加
//                if (Tp_crc_ruleEnum.SAME_CASE_RULE.getId().equals(key)) {
//                    List<Crw_rule_samecase> samecaseRules = crw_rule_samecaseDao.findByRuleIds(ruleGroup.get(key));
//                    checkRuleRedis.setSameCaseRules(samecaseRules);
//                }
            }
        }
    }

    /**
     * 初始化校验规则 -用于规则校验
     * @param personId 人员id
     * @param checkRuleRequestBO
     * @throws Exception
     */
    public void initValidateRule(String personId, CheckRuleRequestBO checkRuleRequestBO) throws Exception {

        synchronized(checkRuleRequestBO) {
            Crc_person personInfo = this.crc_personService.getById(personId);
            final String redisKey = REDIS_KEY + personId;
            CheckRuleRedisBO ruleRedis = new CheckRuleRedisBO();
            if (ObjectUtil.isNull(personInfo)) {
                // 抛出异常
                log.warn("当前人员" + personId + "未保存在社矫人员表中");
            } else {
                try {
                    // 查询人员上设置的规则
                    List<Crw_rule_person> personRules = this.crw_rule_personDao.getRuleByPersonId(personId);
                    // 群组上的规则
                    List<Crw_rule_group> groupRules = new ArrayList<>();
                    // 查询当前的人员的屏蔽规则
                    List<Crw_rule_disabled> disabledRules = this.crw_rule_disabledDao.findDisabledRuleByPersonId(personId);
                    // 查询当前的人员的分组
                    List<Crc_group> groupList = this.crc_groupService.findGroupByPersonId(personId);
                    if (CollectionUtil.isNotEmpty(groupList)) {
                        if (CollectionUtil.isNotEmpty(groupList)) {
                            List<String> groupIds = groupList.stream().map(Crc_group::getId).collect(Collectors.toList());
                            groupRules = this.crw_rule_groupDao.getRuleByGroupIds(groupIds);
                        }
                    }
                    // 进行对象拼装
                    checkRuleRequestBO.setPersonInfo(personInfo);
                    checkRuleRequestBO.setGroupList(groupList);
                    checkRuleRequestBO.setDisabledRules(disabledRules);
                    // 进行规则去重
                    Set<String> ruleSet = new HashSet<>();
                    personRules.stream().forEach(element -> ruleSet.add(element.getRuleid()));
                    groupRules.stream().forEach(element -> ruleSet.add(element.getRuleid()));
                    // 进行规则分组
                    Map<Integer, List<String>> ruleGroup = splitRuleGroup(ruleSet);
                    // 查询全部的规则
                    this.initRuleWithGroup(ruleGroup, checkRuleRequestBO);

                    CompletableFuture.runAsync(() -> {
                        // 进行对象拼装
                        try {
                            ruleRedis.setDisabledRules(disabledRules);
                            ruleRedis.setPersonInfo(personInfo);
                            ruleRedis.setGroupList(groupList);
                            // 查询全部的规则
                            this.initRuleWithGroupRedis(ruleGroup, ruleRedis);
                            // 记录当前的缓存数据
//                            redisCacheUtil.set(redisKey, ruleRedis);
                            log.info("当前人员：{}数据记录如下：{}", personId, JSON.toJSONString(ruleRedis));
                        } catch (Exception e) {
                            log.error("当前人员：{}数据记录异常：{}", personId, e.getMessage());
                            // 删除redis的key
//                            redisCacheUtil.del(redisKey);
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("当前人员：{}初始化校验规则方法异常：{}", personId, e.getMessage());
                    throw e;
                }
            }
        }
    }

    /**
     * 缓存数据查询，用于缓存
     * @param personId
     * @return
     */
    public void initRuleCacheByPersonId(String personId) {
        final String redisKey = REDIS_KEY + personId;
        synchronized (lock.intern(personId)) {
            try {
                Crc_person personInfo = this.crc_personService.getById(personId);
                CheckRuleRedisBO ruleRedis = new CheckRuleRedisBO();
                // 查询人员上设置的规则
                List<Crw_rule_person> personRules = this.crw_rule_personDao.getRuleByPersonId(personId);
                // 群组上的规则
                List<Crw_rule_group> groupRules = new ArrayList<>();
                // 查询当前的人员的屏蔽规则
                List<Crw_rule_disabled> disabledRules = this.crw_rule_disabledDao.findDisabledRuleByPersonId(personId);
                // 查询当前的人员的分组
                List<Crc_group> groupList = this.crc_groupService.findGroupByPersonId(personId);
                if (CollectionUtil.isNotEmpty(groupList)) {
                    if (CollectionUtil.isNotEmpty(groupList)) {
                        List<String> groupIds = groupList.stream().map(Crc_group::getId).collect(Collectors.toList());
                        groupRules = this.crw_rule_groupDao.getRuleByGroupIds(groupIds);
                    }
                }
                // 进行对象拼装
                ruleRedis.setDisabledRules(disabledRules);
                ruleRedis.setPersonInfo(personInfo);
                ruleRedis.setGroupList(groupList);
                // 进行规则去重
                Set<String> ruleSet = new HashSet<>();
                personRules.stream().forEach(element -> ruleSet.add(element.getRuleid()));
                groupRules.stream().forEach(element -> ruleSet.add(element.getRuleid()));
                // 进行规则分组
                Map<Integer, List<String>> ruleGroup = splitRuleGroup(ruleSet);
                // 查询全部的规则
                this.initRuleWithGroupRedis(ruleGroup, ruleRedis);
                // 记录当前的缓存数据
               redisCacheUtil.set(redisKey, ruleRedis);

                log.info("当前数据库操作写入redis缓存数据记录如下，人员：{}，记录：{}", personId, JSON.toJSONString(ruleRedis));
            } catch (Exception e) {
                e.printStackTrace();
                log.error("当前数据库操作写入redis缓存数据异常，人员：{}，异常：{}, 删除对应的缓存数据", personId, e.getMessage());
                redisCacheUtil.del(redisKey);
            }
        }
    }

}
